// Copyright (C) 2011 nitsuja and contributors
// Copyright (C) 2013 Hourglass-Resurrection team
// Hourglass-Resurrection is licensed under GPL v3.
// Full notice is in COPYING.txt.

#if !defined(WINDOWHOOKS_INCL) && !defined(UNITY_BUILD)
#define WINDOWHOOKS_INCL

#include "../wintasee.h"
#include "../tls.h"
#include "../msgqueue.h"
#include "../locale.h"
#include "../gwi.h"
#include <map>

static int createWindowDepth = 0;

std::map<HWND, WNDPROC> hwndToOrigHandler;
//std::map<HWND, BOOL> hwndDeniedDeactivate;
//std::map<HWND, BOOL> hwndRespondingToPaintMessage;

HOOKFUNC LONG WINAPI MyGetWindowLongA(HWND hWnd, int nIndex);
HOOKFUNC LONG WINAPI MyGetWindowLongW(HWND hWnd, int nIndex);
LRESULT CALLBACK MyWndProcA(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MyWndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT DispatchMessageInternal(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool ascii=true, MessageActionFlags maf=MAF_PASSTHROUGH|MAF_RETURN_OS); // extern? (I mean, move to header)

// TODO: Detect CS_PARENTDC when moving to Ex-style DCs.
HOOKFUNC ATOM WINAPI MyRegisterClassA(const WNDCLASSA *lpwc)
{
	if((lpwc->style & CS_OWNDC) || (lpwc->style & CS_CLASSDC)) gwi.bEmulateDC = FALSE;
	return RegisterClassA(lpwc);
}
HOOKFUNC ATOM WINAPI MyRegisterClassW(const WNDCLASSW *lpwc)
{
	if((lpwc->style & CS_OWNDC) || (lpwc->style & CS_CLASSDC)) gwi.bEmulateDC = FALSE;
	return RegisterClassW(lpwc);
}

HOOKFUNC ATOM WINAPI MyRegisterClassExA(const WNDCLASSEXA *lpwcex)
{
	if((lpwcex->style & CS_OWNDC) || (lpwcex->style & CS_CLASSDC)) gwi.bEmulateDC = FALSE;
	return RegisterClassExA(lpwcex);
}
HOOKFUNC ATOM WINAPI MyRegisterClassExW(const WNDCLASSEXW *lpwcex)
{
	if((lpwcex->style & CS_OWNDC) || (lpwcex->style & CS_CLASSDC)) gwi.bEmulateDC = FALSE;
	return RegisterClassExW(lpwcex);
}

// This forced inline will result in a larger DLL, but it will also be more effective as there will be less branch-predictions etc.
HWND __forceinline LocalCreateWindow(DWORD dwExStyle, void *lpClassName, void* lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam, bool unicode)
{
	HWND hwnd;
	WNDPROC pWindowProc;
	bool isValidHandle = true;
	//bool wasReallyFullscreen = false;


	dwStyle &= ~WS_MAXIMIZE;
	// Try to detect full screen games. Will this give false positives on splash screens in games like XIII?
	if((dwStyle & (WS_EX_TOPMOST | WS_POPUP)) || ((dwExStyle & WS_EX_TOPMOST) && (dwStyle & WS_POPUP)))
	{
		gwi.fullScreenGame = true;
	}
	
	if(gwi.hWnd == NULL) // First call. Will splash screens mess this up?
	{
		gwi.originalPosX = X == CW_USEDEFAULT ? 0 : X;
		gwi.originalPosY = Y == CW_USEDEFAULT ? 0 : Y;
	}

	// v2.1.92: fixes size & position for auxiliary big window, often used
	// for intro movies etc. : needed for ......
	// evidently, this was supposed to be a fullscreen window....
	// v2.1.100: fixes for "The Grinch": this game creates a new main window for OpenGL
	// rendering using CW_USEDEFAULT placement and 800x600 size while the previous
	// main win was 640x480 only!
	// v2.02.13: if it's a WS_CHILD window, don't reposition the x,y, placement for BIG win.
	// v2.02.30: fix (Fable - lost chapters) Fable creates a bigger win with negative x,y coordinates. 
	if ((((X <= 0) && (Y <= 0)) || ((X == CW_USEDEFAULT) && (Y == CW_USEDEFAULT)))
		&&
		((nWidth >= gwi.dwScreenWidth) && (nHeight >= gwi.dwScreenHeight))
		&&
		!(dwExStyle & WS_EX_CONTROLPARENT) // Diablo fix
		&&
		!(dwStyle & WS_CHILD)) // Diablo fix
	{
		RECT screen;
		POINT upleft = {0,0};

		// v2.02.30: fix (Fable - lost chapters)
		if(nWidth == CW_USEDEFAULT) nWidth = gwi.dwScreenWidth;
		if(nHeight == CW_USEDEFAULT) nHeight = gwi.dwScreenHeight;

		// // update virtual screen size if it has grown 
		gwi.dwScreenWidth = nWidth;
		gwi.dwScreenHeight = nHeight;

		// inserted some checks here, since the main window could be destroyed
		// or minimized (see "Jedi Outcast") so that you may get a dangerous 
		// zero size. In this case, better renew the hWnd assignement and its coordinates.
		do { // fake loop
			isValidHandle = false;
			if (!GetClientRect(gwi.hWnd, &screen)) break;
			if (!ClientToScreen(gwi.hWnd, &upleft)) break;
			if (screen.right == 0 || screen.bottom == 0) break;
			isValidHandle = true;
		} while(false);

		if (isValidHandle) // use parent's coordinates
		{
			if (!(dwStyle & WS_CHILD)){ 
				X = upleft.x;
				Y = upleft.y;
			}
			nWidth = screen.right - screen.left; // Remember to subtract the left side.
			nHeight = screen.bottom - screen.top;
		}
		else
		{
			// invalid parent coordinates: use initial placement, but leave the size.
			// should also fix the window style and compensate for borders here?
			X = gwi.originalPosX;
			Y = gwi.originalPosY;

			nWidth = gwi.dwScreenWidth;
			nHeight = gwi.dwScreenHeight;

			// isValidHandle can never get set to negative outside of the big if-case
			// Apply changes already before the window is made, saves some function calls.
			// It will not affect the Monster Truck Madness hack further down as this
			// only apply to windows setting the fullScreenGame flag.
			//if ((!isValidHandle) && gwi.fullScreenGame) {
			if(gwi.fullScreenGame)
			{
			//	gwi.SethWnd(hwnd);
			//	extern void AdjustWindowPos(HWND, DWORD, DWORD);
			//	(*pSetWindowLong)(wndh, GWL_STYLE, (dxw.dwFlags2 & MODALSTYLE) ? 0 : WS_OVERLAPPEDWINDOW);
				dwStyle |= WS_OVERLAPPED;
			//	(*pSetWindowLong)(wndh, GWL_EXSTYLE, 0); 
				dwExStyle = 0;
			}
			//	OutTraceD("%s: hwnd=%x, set style=WS_OVERLAPPEDWINDOW extstyle=0\n", ApiName, wndh); 
			//	AdjustWindowPos(wndh, nWidth, nHeight);
			//	(*pShowWindow)(wndh, SW_SHOWNORMAL);
			//}
		}
		//gwi.SetFullScreen(TRUE);
		/*if(dxw.Coordinates==DXW_DESKTOP_WORKAREA){ // What does this do?
			RECT workarea;
			SystemParametersInfo(SPI_GETWORKAREA, NULL, &workarea, 0);
			x=0;
			y=0;
			nWidth=workarea.right;
			nHeight=workarea.bottom;
			dwStyle=0;
		//	OutTraceD("%s: WORKAREA win pos=(%d,%d) size=(%d,%d)\n", ApiName, x, y, nWidth, nHeight);
		}
		else if(dxw.Coordinates==DXW_DESKTOP_FULL){
			RECT workarea;
			(*pGetClientRect)((*pGetDesktopWindow)(), &workarea);
			x=0;
			y=0;
			nWidth=workarea.right;
			nHeight=workarea.bottom;
			dwStyle=0;
		//	OutTraceD("%s: FULLDESKTOP win pos=(%d,%d) size=(%d,%d)\n", ApiName, x, y, nWidth, nHeight);
		}*/
	}

	if(!gwi.fullScreenGame) // v2.1.63: needed for "Monster Truck Madness"
	{
		if(unicode)
		{
			hwnd = CreateWindowExW(dwExStyle, (LPCWSTR)lpClassName, (LPCWSTR)lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
		}
		else
		{
			hwnd = CreateWindowExA(dwExStyle, (LPCSTR)lpClassName, (LPCSTR)lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
		}
		//OutTraceD("%s: windowed mode ret=%x\n", ApiName, wndh);
		return hwnd;
	}

	// tested on Gangsters: coordinates must be window-relative!!!
	// Age of Empires....
	// needed for Diablo, that creates a new control parent window that must be
	// overlapped to the directdraw surface.
	if (dwStyle & WS_CHILD || dwExStyle & WS_EX_CONTROLPARENT)
	{ 
		gwi.MapToWindow(&X, &Y);
	}

	// Always fix up the window frame on game windows that aren't children.
	// TODO: Breaks Diablo?
	if (!(dwStyle & WS_CHILD))
	{
		dwStyle |= WS_OVERLAPPEDWINDOW;
		dwExStyle = 0;
	}

	if(unicode)
	{
		hwnd = CreateWindowExW(dwExStyle, (LPCWSTR)lpClassName, (LPCWSTR)lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
	}
	else
	{
		hwnd = CreateWindowExA(dwExStyle, (LPCSTR)lpClassName, (LPCSTR)lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
	}

	if (hwnd == (HWND)NULL)
	{
	//	OutTraceE("%s: ERROR err=%d Style=%x(%s) ExStyle=%x\n",
	//		ApiName, GetLastError(), dwStyle, ExplainStyle(dwStyle), dwExStyle);
		return hwnd;
	}

	if(!isValidHandle) // Only set this if window creation didn't fail.
	{
		gwi.hWnd = hwnd;
	}
	
	// to do: handle inner child, and leave dialogue & modal child alone!!!
	// Hooking PeekMessage instead.
	/*if ((dwStyle & WS_CHILD) && (dxw.dwFlags1 & HOOKCHILDWIN)){
		long res;
		pWindowProc = (WNDPROC)(*pGetWindowLong)(wndh, GWL_WNDPROC);
		//OutTraceD("Hooking CHILD wndh=%x WindowProc %x->%x\n", wndh, pWindowProc, extChildWindowProc);
		res=(*pSetWindowLong)(wndh, GWL_WNDPROC, (LONG)extChildWindowProc);
		WhndStackPush(wndh, pWindowProc);
		//if(!res) OutTraceE("%s: SetWindowLong ERROR %x\n", ApiName, GetLastError());
	}*/

	//OutTraceD("%s: ret=%x\n", ApiName, wndh);
	return hwnd;
}

HOOKFUNC HWND WINAPI MyCreateWindowExA(DWORD dwExStyle, LPCSTR lpClassName,
	LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight,
	HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
{
	debuglog(LCF_WINDOW|LCF_TODO, __FUNCTION__ "(%d,%d,%d,%d,0x%X,0x%X) called.\n", X,Y,nWidth,nHeight,dwStyle,dwExStyle);
	createWindowDepth++;

	//if(tasflags.forceWindowed && X == 0 && Y == 0 && nWidth > 640 && nHeight > 480)
	//{
	//	// check for exact matches with the screen size
	//	// (because this might be a fake-fullscreen window)
	//	if(nWidth == GetSystemMetrics(SM_CXSCREEN) && nHeight == GetSystemMetrics(SM_CYSCREEN))
	//	{
	//		nWidth = 640;
	//		nHeight = 480;
	//		dwStyle &= ~WS_POPUP;
	//		dwStyle |= WS_CAPTION;
	//	}
	//}

	//HWND oldGamehwnd = gamehwnd;
	//HWND oldGamehwnd = gwi.hWnd;

	ThreadLocalStuff& curtls = tls;
	curtls.callerisuntrusted++;
	curtls.treatDLLLoadsAsClient++;



	//HWND hwnd = CreateWindowExA(dwExStyle, lpClassName,
	//	lpWindowName, dwStyle, X, Y, nWidth, nHeight,
	//	hWndParent, hMenu, hInstance, lpParam);
	HWND hwnd = LocalCreateWindow(dwExStyle, (void*)lpClassName, (void*)lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam, false);

	HOOKFUNC BOOL WINAPI MySetWindowTextA(HWND hWnd, LPCSTR lpString);
	MySetWindowTextA(hwnd, lpWindowName);

	curtls.treatDLLLoadsAsClient--;
	curtls.callerisuntrusted--;
	debuglog(LCF_WINDOW, __FUNCTION__ " made hwnd = 0x%X.\n", hwnd);
#ifdef EMULATE_MESSAGE_QUEUES
	if(hwnd)
	{
		MessageQueue& mq = curtls.messageQueue;
//		if(mq.attachedWindows.empty())
//			mq.attachedWindows.insert((HWND)NULL); // so PostMessage with a NULL HWND knows to post to the current thread
		mq.attachedWindows.push_back(hwnd);
	}
#endif
	createWindowDepth--;
	if(hwnd && createWindowDepth == 0)
	{
		//if(!oldGamehwnd)
		//{
		//	curtls.createdFirstWindow = true;
		//	gamehwnd = hwnd;
		//}

		WNDPROC oldProc = (WNDPROC)MyGetWindowLongA(hwnd, GWL_WNDPROC);
		if(!oldProc)
		{
			WNDCLASSEXA cls = { sizeof(WNDCLASSEXA) };
			GetClassInfoExA(hInstance, lpClassName, &cls);
			if(cls.lpfnWndProc)
			{
				oldProc = cls.lpfnWndProc;
				debuglog(LCF_WINDOW|LCF_TODO, "had to retrieve wndproc from wndclass (\"%s\") for some reason...\n", lpClassName);
			}
		}
		debuglog(LCF_WINDOW, "oldProc[0x%X] = 0x%X\n", hwnd, oldProc);
		hwndToOrigHandler[hwnd] = oldProc;
		SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)MyWndProcA);
		cmdprintf("HWND: %d", hwnd);

		// TODO: This seems weird, should be handled in messaging instead?
		//if(tasflags.windowActivateFlags & 2)
		//{
			// hmm, I'm getting desyncs all of a sudden...
			// the wintaser window flickers every time it happens
			// but I don't know what could be causing that.
			// well, maybe it's happening less now for some reason,
			// but it's something to watch for (possible bugs here)

//			tasflags.windowActivateFlags ^= 2;
//			ShowWindow(hwnd, TRUE);
			SetForegroundWindow(hwnd);
			//SetActiveWindow(hwnd);
			//SetFocus(hwnd);
			SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
//			tasflags.windowActivateFlags ^= 2;
		//}
		//else
		//{
		//	SetWindowPos(hwnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
		//}
		SetActiveWindow(hwnd);


		// FIXME TEMP maybe need to hook SetActiveWindow / SetForegroundWindow etc instead
		DispatchMessageInternal(hwnd, WM_ACTIVATE, WA_ACTIVE, (LPARAM)hwnd);
		DispatchMessageInternal(hwnd, WM_SETFOCUS, 0, 0);
		
		/*WINDOWPOS pos = {
			hwnd,//HWND    hwnd;
			hWndParent,//HWND    hwndInsertAfter;
			X,//int     x;
			Y,//int     y;
			nWidth,//int     cx;
			nHeight,//int     cy;
			SWP_NOREDRAW|SWP_NOACTIVATE|SWP_FRAMECHANGED,//UINT    flags;
		};*/
		//DispatchMessageInternal(hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)&pos);

		CREATESTRUCTA create = {
			lpParam,//LPVOID    lpCreateParams;
			hInstance,//HINSTANCE hInstance;
			hMenu,//HMENU     hMenu;
			hWndParent,//HWND      hwndParent;
			nHeight,//int       cy;
			nWidth,//int       cx;
			Y,//int       y;
			X,//int       x;
			dwStyle,//LONG      style;
			lpWindowName,//LPCTSTR   lpszName;
			lpClassName,//LPCTSTR   lpszClass;
			dwExStyle,//DWORD     dwExStyle;
		};
		DispatchMessageInternal(hwnd, WM_CREATE, 0, (LPARAM)&create);
	}
	return hwnd;
}
HOOKFUNC HWND WINAPI MyCreateWindowExW(DWORD dwExStyle, LPCWSTR lpClassName,
	LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight,
	HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
{
	debuglog(LCF_WINDOW, __FUNCTION__ " called.\n");
	createWindowDepth++;
	//HWND oldGamehwnd = gamehwnd;
	ThreadLocalStuff& curtls = tls;
	curtls.callerisuntrusted++;
	curtls.treatDLLLoadsAsClient++;
	//HWND hwnd = CreateWindowExW(dwExStyle, lpClassName,
	//	lpWindowName, dwStyle, X, Y, nWidth, nHeight,
	//	hWndParent, hMenu, hInstance, lpParam);
	HWND hwnd = LocalCreateWindow(dwExStyle, (void*)lpClassName, (void*)lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam, true);
	curtls.treatDLLLoadsAsClient--;
	curtls.callerisuntrusted--;
	debuglog(LCF_WINDOW, __FUNCTION__ " made hwnd = 0x%X.\n", hwnd);
#ifdef EMULATE_MESSAGE_QUEUES
	if(hwnd)
	{
		MessageQueue& mq = curtls.messageQueue;
//		if(mq.attachedWindows.empty())
//			mq.attachedWindows.insert((HWND)NULL); // so PostMessage with a NULL HWND knows to post to the current thread
		mq.attachedWindows.push_back(hwnd);
	}
#endif
	createWindowDepth--;
#if 0 // FIXME should be enabled but currently breaks Iji due to some bug
	if(hwnd && createWindowDepth == 0)
	{
		if(!oldGamehwnd)
		{
			curtls.createdFirstWindow = true;
			gamehwnd = hwnd;
		}

		//WNDCLASSEXA cls = { sizeof(WNDCLASSEXA) };
		//GetClassInfoExA(hInstance, lpClassName, &cls);

		WNDPROC oldProc = (WNDPROC)MyGetWindowLongW(hwnd, GWL_WNDPROC);
		if(!oldProc)
		{
			WNDCLASSEXW cls = { sizeof(WNDCLASSEXW) };
			GetClassInfoExW(hInstance, lpClassName, &cls);
			if(cls.lpfnWndProc)
			{
				oldProc = cls.lpfnWndProc;
				debuglog(LCF_WINDOW|LCF_TODO, "had to retrieve wndproc from wndclass (\"%S\") for some reason...\n", lpClassName);
			}
		}
		debuglog(LCF_WINDOW, "oldProc[0x%X] = 0x%X\n", hwnd, oldProc);
		debuglog(LCF_WINDOW, "oldProc[0x%X] = 0x%X\n", hwnd, oldProc);
		hwndToOrigHandler[hwnd] = oldProc;
		SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)MyWndProcW);
		cmdprintf("HWND: %d", hwnd);

		if(tasflags.windowActivateFlags & 2)
		{
			// hmm, I'm getting desyncs all of a sudden...
			// the wintaser window flickers every time it happens
			// but I don't know what could be causing that.
			// well, maybe it's happening less now for some reason,
			// but it's something to watch for (possible bugs here)

//			tasflags.windowActivateFlags ^= 2;
//			ShowWindow(hwnd, TRUE);
			SetForegroundWindow(hwnd);
			//SetActiveWindow(hwnd);
			//SetFocus(hwnd);
			SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
//			tasflags.windowActivateFlags ^= 2;
		}
		else
		{
			SetWindowPos(hwnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
		}
		SetActiveWindow(hwnd);


		// FIXME TEMP maybe need to hook SetActiveWindow / SetForegroundWindow etc instead
		DispatchMessageInternal(hwnd, WM_ACTIVATE, WA_ACTIVE, (LPARAM)hwnd, false);
		DispatchMessageInternal(hwnd, WM_SETFOCUS, 0, 0, false);

		/*WINDOWPOS pos = {
			hwnd,//HWND    hwnd;
			hWndParent,//HWND    hwndInsertAfter;
			X,//int     x;
			Y,//int     y;
			nWidth,//int     cx;
			nHeight,//int     cy;
			SWP_NOREDRAW|SWP_NOACTIVATE|SWP_FRAMECHANGED,//UINT    flags;
		};*/
		//SendMessageW(hwnd, toggleWhitelistMessage(WM_WINDOWPOSCHANGED), 0, (LPARAM)&pos);

		CREATESTRUCTW create = {
			lpParam,//LPVOID    lpCreateParams;
			hInstance,//HINSTANCE hInstance;
			hMenu,//HMENU     hMenu;
			hWndParent,//HWND      hwndParent;
			nHeight,//int       cy;
			nWidth,//int       cx;
			Y,//int       y;
			X,//int       x;
			dwStyle,//LONG      style;
			lpWindowName,//LPCTSTR   lpszName;
			lpClassName,//LPCTSTR   lpszClass;
			dwExStyle,//DWORD     dwExStyle;
		};
		DispatchMessageInternal(hwnd, WM_CREATE, 0, (LPARAM)&create, false);

		// trying to get the stupid splash screen to work, not sure how to fake the paint event well enough for it
		//InvalidateRect(hwnd, NULL, TRUE);
		//PostMessageInternal(hwnd, WM_PAINT, 0, 0, false);
		//InvalidateRect(hwnd, NULL, TRUE);
		//DispatchMessageInternal(hwnd, WM_PAINT, 0, 0, false);
	}
#endif
	return hwnd;
}

HOOKFUNC BOOL WINAPI MyCloseWindow(HWND hWnd)
{
	debuglog(LCF_WINDOW, __FUNCTION__ "(0x%X) called.\n", hWnd);
	BOOL rv = CloseWindow(hWnd);
	if(hWnd == gwi.hWnd)
	{
		gwi.hWnd = NULL;
	}
	return rv;
}

HOOKFUNC BOOL WINAPI MyDestroyWindow(HWND hWnd)
{
	debuglog(LCF_WINDOW, __FUNCTION__ "(0x%X) called.\n", hWnd);
	ThreadLocalStuff& curtls = tls;
	curtls.destroyWindowDepth++;
	BOOL rv = DestroyWindow(hWnd);
	curtls.destroyWindowDepth--;
#ifdef EMULATE_MESSAGE_QUEUES
	if(rv)
	{
		MessageQueue& mq = tls.messageQueue;
		std::vector<HWND>& v = tls.messageQueue.attachedWindows;
		v.erase(std::remove(v.begin(), v.end(), hWnd), v.end());
	}
#endif
	// Because Empire Earth likes to create many small test windows first, according to DxWnd.
	if(hWnd == gwi.hWnd)
	{
		gwi.hWnd = NULL;
	}
	return rv;
}

HOOKFUNC HWND WINAPI MyGetActiveWindow()
{
	debuglog(LCF_WINDOW/*|LCF_DESYNC*/|LCF_FREQUENT, __FUNCTION__ " called (0x%X).\n", gwi.hWnd);
	return gwi.hWnd;
}
HOOKFUNC HWND WINAPI MyGetForegroundWindow()
{
	debuglog(LCF_WINDOW/*|LCF_DESYNC*/|LCF_FREQUENT, __FUNCTION__ " called (0x%X).\n", gwi.hWnd);
	return gwi.hWnd;
}
HOOKFUNC HWND WINAPI MyGetFocus()
{
	debuglog(LCF_WINDOW/*|LCF_DESYNC*/|LCF_FREQUENT, __FUNCTION__ " called (0x%X).\n", gwi.hWnd);
	return gwi.hWnd;
}
HOOKFUNC HWND WINAPI MyGetTopWindow(HWND hWnd)
{
	if(gwi.fullScreenGame && gwi.IsDesktop(hWnd)) return hWnd;

	return GetTopWindow(hWnd);
}


HOOKFUNC LONG WINAPI MyGetWindowLongA(HWND hWnd, int nIndex)
{
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "(%d) called on 0x%X.\n", nIndex, hWnd);
	if(nIndex == GWL_WNDPROC)
	{
		std::map<HWND, WNDPROC>::iterator found = hwndToOrigHandler.find(hWnd);
		if(found != hwndToOrigHandler.end())
		{
			debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ " rV = 0x%X.\n", found->second);
			return (LONG)found->second;
		}
		else
		{
			LONG rv = GetWindowLongA(hWnd, nIndex);
			debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ " rv! = 0x%X.\n", rv);
			return rv;
		}
	}
	LONG rv = GetWindowLongA(hWnd, nIndex);
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ " Rv = 0x%X.\n", rv);
	return rv;
}
HOOKFUNC LONG WINAPI MyGetWindowLongW(HWND hWnd, int nIndex)
{
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "(%d) called on 0x%X.\n", nIndex, hWnd);
	if(nIndex == GWL_WNDPROC)
	{
		std::map<HWND, WNDPROC>::iterator found = hwndToOrigHandler.find(hWnd);
		if(found != hwndToOrigHandler.end())
		{
			return (LONG)found->second;
			debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ " rV = 0x%X.\n", found->second);
		}
		else
		{
			LONG rv = GetWindowLongW(hWnd, nIndex);
			debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ " rv! = 0x%X.\n", rv);
			return rv;
		}
	}
	LONG rv = GetWindowLongW(hWnd, nIndex);
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ " Rv = 0x%X.\n", rv);
	return rv;
}


HOOKFUNC LONG WINAPI MySetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong)
{
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "(%d, 0x%X) called on 0x%X.\n", nIndex, dwNewLong, hWnd);
	if(nIndex == GWL_WNDPROC)
	{
		// Grand Prix Legends fix, advice taken from DxWnd.
		// Some programs apparently call this function with a NULL/Desktop hWnd
		if(gwi.IsRealDesktop(hWnd))
			hWnd = gwi.hWnd;
		// the game is trying to change this window's procedure.
		// since we need it to stay replaced with our own winproc,
		// update our pointer to the original winproc instead.
		LONG rv = MyGetWindowLongA(hWnd, nIndex);
		debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "hwndToOrigHandler[0x%X] = 0x%X.\n", hWnd, dwNewLong);
		debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "rv = 0x%X.\n", rv);
		hwndToOrigHandler[hWnd] = (WNDPROC)dwNewLong;
		SetWindowLongA(hWnd, GWL_WNDPROC, (LONG)MyWndProcA);
		return rv;
	}
	if(nIndex == GWL_STYLE || GWL_EXSTYLE)
	{
		
		// some SDL apps create a window, attach d3d,
		// then modify the window style for fullscreen.
		// disallow that last step if the window has been fake-fullscreen locked.
		//if(IsWindowFakeFullscreen(hWnd))
		//{
		LONG rv = MyGetWindowLongA(hWnd, nIndex);
		LONG diff = rv & dwNewLong;
		// Don't let the game maximize the window
		if(diff & WS_MAXIMIZE)
		{
			diff &= ~WS_MAXIMIZE;
			if(gwi.IsDesktop(hWnd))
			{
			//	OutTraceD("SetWindowLong: GWL_STYLE %x suppress MAXIMIZE\n", dwNewLong);

				diff |= WS_OVERLAPPEDWINDOW; 
				diff &= ~(WS_DLGFRAME/*|WS_MAXIMIZE*/|WS_VSCROLL|WS_HSCROLL|WS_CLIPSIBLINGS|WS_EX_TOPMOST); 
			}
		}
		if(!(diff & WS_CHILD))
		{
			diff |= WS_OVERLAPPEDWINDOW; 
			diff &= ~WS_CLIPSIBLINGS; 
		}
		// If there are still changes to be made, apply those, otherwise do nothing.
		if(diff != rv) return SetWindowLongA(hWnd, nIndex, rv | diff);
		else return rv;
		//}
	}
	
	LONG rv = SetWindowLongA(hWnd, nIndex, dwNewLong);
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "RV = 0x%X.\n", rv);
	return rv;
}
HOOKFUNC LONG WINAPI MySetWindowLongW(HWND hWnd, int nIndex, LONG dwNewLong)
{
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "(%d, 0x%X) called on 0x%X.\n", nIndex, dwNewLong, hWnd);
	if(nIndex == GWL_WNDPROC)
	{
		// Grand Prix Legends fix, advice taken from DxWnd.
		// Some programs apparently call this function with a NULL/Desktop hWnd
		if(gwi.IsRealDesktop(hWnd))
			hWnd = gwi.hWnd;
		// the game is trying to change this window's procedure.
		// since we need it to stay replaced with our own winproc,
		// update our pointer to the original winproc instead.
		LONG rv = MyGetWindowLongW(hWnd, nIndex);
		debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "hwndToOrigHandler[0x%X] = 0x%X.\n", hWnd, dwNewLong);
		debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "rv = 0x%X.\n", rv);
		hwndToOrigHandler[hWnd] = (WNDPROC)dwNewLong;
		SetWindowLongW(hWnd, GWL_WNDPROC, (LONG)MyWndProcW);
		return rv;
	}
	if(nIndex == GWL_STYLE || nIndex == GWL_EXSTYLE)
	{
		// some SDL apps create a window, attach d3d,
		// then modify the window style for fullscreen.
		// disallow that last step if the window has been fake-fullscreen locked.
		//if(IsWindowFakeFullscreen(hWnd))
		//{
		LONG rv = MyGetWindowLongW(hWnd, nIndex);
		LONG diff = rv & dwNewLong;
		// Don't let the game maximize the window
		if(diff & WS_MAXIMIZE)
		{
			diff &= ~WS_MAXIMIZE;
			if(gwi.IsDesktop(hWnd))
			{
			//	OutTraceD("SetWindowLong: GWL_STYLE %x suppress MAXIMIZE\n", dwNewLong);

				diff |= WS_OVERLAPPEDWINDOW; 
				diff &= ~(WS_DLGFRAME/*|WS_MAXIMIZE*/|WS_VSCROLL|WS_HSCROLL|WS_CLIPSIBLINGS|WS_EX_TOPMOST); 
			}
		}
		if(!(diff & WS_CHILD))
		{
			diff |= WS_OVERLAPPEDWINDOW; 
			diff &= ~WS_CLIPSIBLINGS; 
		}
		// If there are still changes to be made, apply those, otherwise do nothing.
		if(diff != rv) return SetWindowLongW(hWnd, nIndex, rv | diff);
		else return rv;
		//}
	}
	LONG rv = SetWindowLongW(hWnd, nIndex, dwNewLong);
	debuglog(LCF_WINDOW|LCF_FREQUENT, __FUNCTION__ "RV = 0x%X.\n", rv);
	return rv;
}

// TODO: Detect CS_PARENTDC changes when moving to Ex-style DCs.
HOOKFUNC DWORD WINAPI MySetClassLongA(HWND hWnd, int nIndex, LONG dwNewLong)
{
	if(nIndex == GCL_STYLE)
	{
		if((dwNewLong & CS_OWNDC) || (dwNewLong & CS_CLASSDC)) gwi.bEmulateDC = TRUE;
		else gwi.bEmulateDC = FALSE;
	}
	return SetClassLongA(hWnd, nIndex, dwNewLong);
}
HOOKFUNC DWORD WINAPI MySetClassLongW(HWND hWnd, int nIndex, LONG dwNewLong)
{
	if(nIndex == GCL_STYLE)
	{
		if((dwNewLong & CS_OWNDC) || (dwNewLong & CS_CLASSDC)) gwi.bEmulateDC = TRUE;
		else gwi.bEmulateDC = FALSE;
	}
	return SetClassLongW(hWnd, nIndex, dwNewLong);
}
HOOKFUNC ULONG_PTR WINAPI MySetClassLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
{
	if(nIndex == GCL_STYLE)
	{
		if((dwNewLong & CS_OWNDC) || (dwNewLong & CS_CLASSDC)) gwi.bEmulateDC = TRUE;
		else gwi.bEmulateDC = FALSE;
	}
	return SetClassLongPtrA(hWnd, nIndex, dwNewLong);
}
HOOKFUNC ULONG_PTR WINAPI MySetClassLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
{
	if(nIndex == GCL_STYLE)
	{
		if((dwNewLong & CS_OWNDC) || (dwNewLong & CS_CLASSDC)) gwi.bEmulateDC = TRUE;
		else gwi.bEmulateDC = FALSE;
	}
	return SetClassLongPtrW(hWnd, nIndex, dwNewLong);
}

HOOKFUNC BOOL WINAPI MyMoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint)
{
	debuglog(LCF_WINDOW|LCF_TODO, __FUNCTION__ "(0x%X, %d, %d, %d, %d, %d) called.\n", hWnd, X, Y, nWidth, nHeight, bRepaint);
//	if(tasflags.forceWindowed)
//	{
//		if(gwi.fullScreenGame) // Still need to keep track of this detail.
//		{
//			RECT rect;
//			if(GetWindowRect(hWnd, &rect))
//			{
//				nWidth = rect.right - rect.left;
//				nHeight = rect.bottom - rect.top;
//			}
//		}
//	}
//	BOOL rv = MoveWindow(hWnd, X, Y, nWidth, nHeight, bRepaint);
//	return rv;
	BOOL ret;

	// Emergency Fighters For Life attempts to move itself or the desktop according to DxWnd.
	// Unknown what they are trying to do with this, DxWnd guesses it's related to video stretching.
	if(gwi.IsDesktop(hWnd))
	{
		//OutTraceD("MoveWindow: prevent moving desktop win\n");
		return TRUE;
	}

	if(gwi.fullScreenGame)
	{
		if((GetWindowLong(hWnd, GWL_STYLE) & WS_CHILD) != WS_CHILD)
		{
			POINT upleft = { 0, 0 };
			RECT client;
			ClientToScreen(gwi.hWnd, &upleft);
			GetClientRect(gwi.hWnd, &client);
		
			X = upleft.x;
			Y = upleft.y;
		}
	}
	else
	{
		// TODO: Set fullScreenGame in this case if it wasn't intended to be created with a window frame?
		// TODO 2: Adjust window size for fullScreenGame to take window frame into account?
		// TODO 3: Keep track of windows' original styles?
		if((X == 0) && (Y == 0) && (nWidth == gwi.dwScreenWidth) && (nHeight == gwi.dwScreenHeight))
		{
			// evidently, this was supposed to be a fullscreen window....
			RECT screen;
			POINT upleft = { 0, 0 };
			GetClientRect(gwi.hWnd, &screen);
			ClientToScreen(gwi.hWnd, &upleft);
			X = upleft.x;
			Y = upleft.y;
			nWidth = screen.right;
			nHeight = screen.bottom;
		}
	}

	ret = MoveWindow(hWnd, X, Y, nWidth, nHeight, bRepaint);
	return ret;
}

HOOKFUNC BOOL WINAPI MySetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags)
{
	//if(tasflags.forceWindowed)
	//{
	//	if(IsWindowFakeFullscreen(hWnd))
	//		uFlags |= SWP_NOMOVE | SWP_NOSIZE;
	//}

	// Commandos fix, advice taken from DxWnd
	// Apparently Commados resizes it's screen to 1x1 pixel no matter what the SetWindowsPos parameters are.
	// TODO: Bug caused by DxWnd? If not, will this affect other games? Special game fix? I'd really don't wanna do special game hacks.
	// -- Warepire
	if(gwi.fullScreenGame)
		return TRUE;

	//if(tasflags.windowActivateFlags & 2)
	//{
	if(hWndInsertAfter == HWND_NOTOPMOST || hWndInsertAfter == HWND_BOTTOM || hWndInsertAfter == HWND_TOP || hWndInsertAfter == NULL)
		hWndInsertAfter = HWND_TOPMOST;
	//}
	//else
	//{
	//	if(hWndInsertAfter == HWND_TOPMOST)
	//		hWndInsertAfter = HWND_NOTOPMOST;
	//}

	// Prevent the window getting bigger than our fake screen size.
	int MaxX = gwi.iSizX;
	int MaxY = gwi.iSizY;
	if (!MaxX) MaxX = gwi.dwScreenWidth;
	if (!MaxY) MaxY = gwi.dwScreenHeight;
	if(cx>MaxX) cx=MaxX;
	if(cy>MaxY) cy=MaxY;

	BOOL rv = SetWindowPos(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);

	// SetWindowPos normally sends WM_WINDOWPOSCHANGED,
	// but to ensure that other things we don't control can't also cause that message to get processed,
	// we block it in GetMessageActionFlags, and send a special version of it that won't get blocked here.
	WINDOWPOS pos = {hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags,};
//	if(fakeDisplayValid && IsWindowFakeFullscreen(hWnd))
//		DispatchMessageInternal(hWnd, WM_WINDOWPOSCHANGED-2, 0, (LPARAM)&pos);
//	else
		DispatchMessageInternal(hWnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)&pos);

	return rv;
}

//Heavily used by "Imperialism II" according to DxWnd.
HOOKFUNC HDWP WINAPI MyDeferWindowPos(HDWP hWinPosInfo, HWND hWnd, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags)
{
	if(gwi.fullScreenGame)
	{
		gwi.MapToWindow(&x, &y);
	}

	return DeferWindowPos(hWinPosInfo, hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
}

HOOKFUNC BOOL WINAPI MyShowWindow(HWND hWnd, int nCmdShow)
{
	if(/*tasflags.forceWindowed && */nCmdShow == SW_MAXIMIZE)
		nCmdShow = SW_SHOWNORMAL;
	BOOL rv = ShowWindow(hWnd, nCmdShow);
	return 1;
}

HOOKFUNC BOOL WINAPI MyGetClientRect(HWND hWnd, LPRECT lpRect)
{
	// IsWindowFakeFullscreen checks disabled because they let window position info leak to Eternal Daughter and possibly others (maybe need to use ::IsChild inside IsWindowFakeFullscreen)
	// VerifyIsTrustedCaller checks added as a hack so that DirectDrawClipper can still get the real window coordinates
	// TODO: instead of calling VerifyIsTrustedCaller we could probably have MyDirectDrawSurface::MyBlt set a flag for us. although maybe this way is safer.
	//if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted))
	if(gwi.IsDesktop(hWnd) && gwi.fullScreenGame)
	{
		if(!lpRect)
			return FALSE;
		lpRect->left = 0;
		lpRect->top = 0;
		lpRect->right = gwi.dwScreenWidth; //fakeDisplayWidth;
		lpRect->bottom = gwi.dwScreenHeight; //fakeDisplayHeight;
		return TRUE;
	}
	//TODO: Since games believe they are covering the entire screen, should we do something else here?
	return GetClientRect(hWnd, lpRect);
}
HOOKFUNC BOOL WINAPI MyGetWindowRect(HWND hWnd, LPRECT lpRect)
{
	// see coments in MyGetClientRect
	//if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted))
	if(gwi.IsDesktop(hWnd) && gwi.fullScreenGame)
	{
		if(!lpRect)
			return FALSE;
		lpRect->left = 0;
		lpRect->top = 0;
		lpRect->right = gwi.dwScreenWidth; //fakeDisplayWidth;
		lpRect->bottom = gwi.dwScreenHeight; //fakeDisplayHeight;
		return TRUE;
	}
	//TODO: Since games believe they are covering the entire screen, should we do something else here?
	return GetWindowRect(hWnd, lpRect);
}

HOOKFUNC BOOL WINAPI MyClientToScreen(HWND hWnd, LPPOINT lpPoint)
{
	// see coments in MyGetClientRect
	//if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted))
	
	// Window is minimized. Might not apply to ClientToScreen
	if (lpPoint && (lpPoint->x == -32000) && (lpPoint->y == -32000)) return TRUE;

	if(gwi.IsDesktop(hWnd) && gwi.fullScreenGame)
	{
		return (lpPoint != NULL);
	}
	
	if(lpPoint == NULL) return FALSE;

	// The clipping region is an area outside the game window that we consider a part of the game area.
	// This is for windowed games that for example detect if the mouse is outside the window when they
	// decide if they shall scroll the screen in any direction.
	// This is not relevant for full-screen games as they are never meant to have the mouse exist the game area.
	static int clipping_region = 0;

	// Positions on actual screen
	// TODO: Keep main window information locally?
	RECT pos_main;
	RECT pos_this; // On top of main
	if(gwi.fullScreenGame)
	{
		GetClientRect(hWnd, &pos_this); // GetWindowRect??
		GetClientRect(gwi.hWnd, &pos_main);
		clipping_region = 0; // Do not allow clipping if we're a full-screen game.
	}
	else
	{
		GetWindowRect(hWnd, &pos_this);
		GetWindowRect(gwi.hWnd, &pos_main);
		clipping_region = 4; // Just some arbitrary value... According to DxWnd it's a good one
	}

	// Clamp
	if(lpPoint->x < pos_main.left - clipping_region) lpPoint->x = 0 - clipping_region;
	else if(lpPoint->x > pos_main.right + clipping_region) lpPoint->x = gwi.dwScreenWidth + clipping_region;
	if(lpPoint->y < pos_main.top - clipping_region) lpPoint->y = 0 - clipping_region;
	else if(lpPoint->y > pos_main.bottom + clipping_region) lpPoint->y = gwi.dwScreenHeight + clipping_region;

	if(lpPoint->x < pos_this.left) lpPoint->x -= pos_this.left;
	if(lpPoint->y < pos_this.top) lpPoint->y -= pos_this.top;

	return TRUE;
	//return ClientToScreen(hWnd, lpPoint);
}
HOOKFUNC BOOL WINAPI MyScreenToClient(HWND hWnd, LPPOINT lpPoint)
{
	// Window is minimized.
	if (lpPoint && (lpPoint->x == -32000) && (lpPoint->y == -32000)) return TRUE;

	if(lpPoint == NULL) return FALSE;

	// see coments in MyGetClientRect
	//if(fakeDisplayValid/* && IsWindowFakeFullscreen(hWnd)*/ && VerifyIsTrustedCaller(!tls.callerisuntrusted))
	if(gwi.IsDesktop(hWnd) && gwi.fullScreenGame)
	{
		return TRUE;
	}

	// TODO: Could this potentially fuzz with our mouse input? The mouse input is already relative to the games' main window.

	// Child window is ontop of the main window, get the offset for the child window to the main window.
	// The offset is automatically sign inverted compared to coordinates (i.e. we're storing the difference between -10 and -60 as 50).
	POINT desktop_offset = { 0, 0 };
	POINT child_offset = { 0, 0 };
	ScreenToClient(gwi.hWnd, &desktop_offset);
	ScreenToClient(hWnd, &child_offset);
	child_offset.x -= desktop_offset.x;
	child_offset.y -= desktop_offset.y;

	ScreenToClient(hWnd, lpPoint);
	lpPoint->x -= child_offset.x;
	lpPoint->y -= child_offset.y;
	
	return TRUE;

	//TODO: Since games believe they are covering the entire screen, should we do something else here?
//	return ScreenToClient(hWnd, lpPoint);
}

HOOKFUNC int WINAPI MyMapWindowPoints(HWND hWndFrom, HWND hWndTo, LPPOINT lpPoints, UINT cPoints)
{
	// According to DxWnd this function is used a lot by games like Commandos 2 and Alien Nations.

	// Since this function accepts desktop hWnds we need to check for that, and if it is one,
	// we have to replace them with the main window hWnd since games think they cover the entire screen.
	if(gwi.IsRealDesktop(hWndFrom)) hWndFrom = gwi.hWnd;
	if(gwi.IsRealDesktop(hWndTo)) hWndFrom = gwi.hWnd;

	return MapWindowPoints(hWndFrom, hWndTo, lpPoints, cPoints);
}

HOOKFUNC BOOL WINAPI MySetWindowTextW(HWND hWnd, LPCWSTR lpString);

HOOKFUNC BOOL WINAPI MySetWindowTextA(HWND hWnd, LPCSTR lpString)
{
	debuglog(LCF_WINDOW, __FUNCTION__ "(0x%X, \"%s\") called.\n", hWnd, lpString);
	if(tasflags.appLocale)
	{
		str_to_wstr(wstr, lpString, LocaleToCodePage(tasflags.appLocale));
		BOOL rv = MySetWindowTextW(hWnd, wstr);
		DispatchMessageInternal(hWnd, WM_SETTEXT, 0, (LPARAM)wstr, false, MAF_BYPASSGAME|MAF_RETURN_OS);
		return rv;
	}
	BOOL rv = SetWindowTextA(hWnd, lpString);
	DispatchMessageInternal(hWnd, WM_SETTEXT, 0, (LPARAM)lpString, true, MAF_BYPASSGAME|MAF_RETURN_OS);
	return rv;
}
HOOKFUNC BOOL WINAPI MySetWindowTextW(HWND hWnd, LPCWSTR lpString)
{
	debuglog(LCF_WINDOW, __FUNCTION__ "(0x%X, \"%S\") called.\n", hWnd, lpString);
	BOOL rv = SetWindowTextW(hWnd, lpString);
	DispatchMessageInternal(hWnd, WM_SETTEXT, 0, (LPARAM)lpString, false, MAF_BYPASSGAME|MAF_RETURN_OS);
	return rv;
}

HOOKFUNC BOOL WINAPI MyUpdateWindow(HWND hWnd)
{
	if(gwi.IsRealDesktop(hWnd))
	{
		hWnd = gwi.hWnd;
	}

	return UpdateWindow(hWnd);
}

// TODO: Actually let the game draw the message boxes instead.
// Should become more manageable after the DxWnd merge.
int GetDefaultMessageBoxResult(UINT uType)
{
	if((uType & MB_DEFBUTTON2) == MB_DEFBUTTON2)
	{
		switch(uType & 0xF)
		{
		case MB_OKCANCEL:
		case MB_RETRYCANCEL:
			return IDCANCEL;
		case MB_ABORTRETRYIGNORE:
		case MB_CANCELTRYCONTINUE:
			return IDRETRY;
		case MB_YESNO:
		case MB_YESNOCANCEL:
			return IDNO;
		}
	}
	if((uType & MB_DEFBUTTON2) == MB_DEFBUTTON3)
	{
		switch(uType & 0xF)
		{
		case MB_ABORTRETRYIGNORE:
			return IDIGNORE;
		case MB_CANCELTRYCONTINUE:
			return IDCONTINUE;
		}
	}
	switch(uType & 0xF)
	{
	default:
	case MB_OK:
	case MB_OKCANCEL:
		return IDOK;
	case MB_YESNO:
	case MB_YESNOCANCEL:
		return IDYES;
	// instead of choosing button 1, let's try the most "keep going" option
	case MB_ABORTRETRYIGNORE:
		return IDIGNORE;
	case MB_RETRYCANCEL:
		return IDCANCEL;
	case MB_CANCELTRYCONTINUE:
		return IDCONTINUE;
	}
}

// TODO: better support for messageboxes and dialog boxes (like, draw them, allow a choice, and record a frame of input for them)
HOOKFUNC int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
	debugprintf(__FUNCTION__ "(\"%s\", \"%s\")\n", lpCaption, lpText);
	cmdprintf("SHORTTRACE: 3,50");
	return GetDefaultMessageBoxResult(uType);
}
HOOKFUNC int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
	debugprintf(__FUNCTION__ "(\"%S\", \"%S\")\n", lpCaption, lpText);
	cmdprintf("SHORTTRACE: 3,50");
	return GetDefaultMessageBoxResult(uType);
}
HOOKFUNC int WINAPI MyMessageBoxExA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, WORD wLanguageId)
{
	debugprintf(__FUNCTION__ "(\"%s\", \"%s\")\n", lpCaption, lpText);
	return GetDefaultMessageBoxResult(uType);
}
HOOKFUNC int WINAPI MyMessageBoxExW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, WORD wLanguageId)
{
	debugprintf(__FUNCTION__ "(\"%S\", \"%S\")\n", lpCaption, lpText);
	return GetDefaultMessageBoxResult(uType);
}
HOOKFUNC INT_PTR WINAPI MyDialogBoxParamA(HINSTANCE hInstance,LPCSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam)
{
	debugprintf(__FUNCTION__ " called.\n");
	return IDOK;
}
HOOKFUNC INT_PTR WINAPI MyDialogBoxParamW(HINSTANCE hInstance,LPCWSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam)
{
	debugprintf(__FUNCTION__ " called.\n");
	return IDOK;
}
HOOKFUNC INT_PTR WINAPI MyDialogBoxIndirectParamA(HINSTANCE hInstance,LPCDLGTEMPLATEA hDialogTemplate,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam)
{
	debugprintf(__FUNCTION__ " called.\n");
	return IDOK;
}
HOOKFUNC INT_PTR WINAPI MyDialogBoxIndirectParamW(HINSTANCE hInstance,LPCDLGTEMPLATEW hDialogTemplate,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam)
{
	debugprintf(__FUNCTION__ " called.\n");
	return IDOK;
}

void ApplyWindowIntercepts()
{
	static const InterceptDescriptor intercepts [] = 
	{
		MAKE_INTERCEPT(1, USER32, RegisterClassA),
		MAKE_INTERCEPT(1, USER32, RegisterClassW),
		MAKE_INTERCEPT(1, USER32, RegisterClassExA),
		MAKE_INTERCEPT(1, USER32, RegisterClassExW),
		MAKE_INTERCEPT(1, USER32, CreateWindowExA),
		MAKE_INTERCEPT(1, USER32, CreateWindowExW), // FIXME?
		MAKE_INTERCEPT(1, USER32, DestroyWindow),
		MAKE_INTERCEPT(1, USER32, CloseWindow),
		MAKE_INTERCEPT(1, USER32, GetActiveWindow),
		MAKE_INTERCEPT(1, USER32, GetForegroundWindow),
		MAKE_INTERCEPT(1, USER32, GetFocus),
		MAKE_INTERCEPT(1, USER32, GetTopWindow),
		MAKE_INTERCEPT(1, USER32, SetWindowLongA),
		MAKE_INTERCEPT(1, USER32, SetWindowLongW),
		MAKE_INTERCEPT(1, USER32, SetClassLongA),
		MAKE_INTERCEPT(1, USER32, SetClassLongW),
		MAKE_INTERCEPT(1, USER32, SetClassLongPtrA),
		MAKE_INTERCEPT(1, USER32, SetClassLongPtrW),
		MAKE_INTERCEPT(1, USER32, GetWindowLongA),
		MAKE_INTERCEPT(1, USER32, GetWindowLongW),
		MAKE_INTERCEPT(1, USER32, MoveWindow),
		MAKE_INTERCEPT(1, USER32, SetWindowPos),
		MAKE_INTERCEPT(1, USER32, DeferWindowPos),
		MAKE_INTERCEPT(1, USER32, ShowWindow),
		MAKE_INTERCEPT(1, USER32, GetClientRect),
		MAKE_INTERCEPT(1, USER32, GetWindowRect),
		MAKE_INTERCEPT(1, USER32, ClientToScreen),
		MAKE_INTERCEPT(1, USER32, ScreenToClient),
		MAKE_INTERCEPT(1, USER32, MapWindowPoints),
		MAKE_INTERCEPT(1, USER32, SetWindowTextA),
		MAKE_INTERCEPT(1, USER32, SetWindowTextW),
		//MAKE_INTERCEPT(1, USER32, InvalidateRect),
		MAKE_INTERCEPT(1, USER32, UpdateWindow),
		MAKE_INTERCEPT(1, USER32, MessageBoxA),
		MAKE_INTERCEPT(1, USER32, MessageBoxW),
		MAKE_INTERCEPT(1, USER32, MessageBoxExA),
		MAKE_INTERCEPT(1, USER32, MessageBoxExW),
		MAKE_INTERCEPT(1, USER32, DialogBoxParamA),
		MAKE_INTERCEPT(1, USER32, DialogBoxParamW),
		MAKE_INTERCEPT(1, USER32, DialogBoxIndirectParamA),
		MAKE_INTERCEPT(1, USER32, DialogBoxIndirectParamW),
	};
	ApplyInterceptTable(intercepts, ARRAYSIZE(intercepts));
}

#else
#pragma message(__FILE__": (skipped compilation)")
#endif
